﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using TGJ_GGA;

namespace BAGA
{
    class Program
    {
        private static StreamWriter sw = null;
        private static double bestfitnesssofar = double.MaxValue;

        private static double[,] data = new double[10, 2];

        private static Random rnd = new Random();

        static void Main(string[] args)
        {
            GAInit gainit = new GAInit();
            gainit.numberOfGenerations = int.MaxValue;
            gainit.populationSize = 25;
            gainit.geneSizeInBytes = 8;
            gainit.elitsm = true;
            gainit.aimForHighFitness = false;
            gainit.keep = 25;
            gainit.allowClones = true;

            data[0, 0] = 0; data[0, 1] = 0;
            data[1, 0] = 1; data[1, 1] = 2;
            data[2, 0] = 2; data[2, 1] = 6;
            data[3, 0] = 3; data[3, 1] = 12;
            data[4, 0] = 4; data[4, 1] = 20;
            data[5, 0] = 5; data[5, 1] = 30;
            data[6, 0] = 6; data[6, 1] = 42;
            data[7, 0] = 7; data[7, 1] = 56;
            data[8, 0] = 8; data[8, 1] = 72;
            data[9, 0] = 9; data[9, 1] = 90;

            sw = new StreamWriter("data x10.dat", false);

            ByteArrayGA BAGA = new ByteArrayGA(gainit, Fitit, ForeachGeneration);

            //BAGA.manipulatorHandler.add(BAGA.ByteCrossover, 0.85, "BitToggle");
            BAGA.manipulatorHandler.add(MyCrossover, 0.85, "MyCrossover");
            //BAGA.manipulatorHandler.add(MyCopy, 0.015, "MyCopy");
            
            //BAGA.manipulatorHandler.add(BAGA.ByteToggle, 0.015, "BitToggle");
            BAGA.manipulatorHandler.add(BAGA.Substitution, 0.015, "Substitution");
            //BAGA.manipulatorHandler.add(BAGA.ByteSubSubstitution, 0.01, "BitSubSubstitution");
            //BAGA.manipulatorHandler.add(BAGA.ByteSwap, 0.50, "swap");
            //BAGA.manipulatorHandler.add(inc, 0.32, "dec first");
            //BAGA.manipulatorHandler.add(dec, 0.32, "inc first");
            //BAGA.manipulatorHandler.add(downPercent, 0.05, "dec first");
            BAGA.manipulatorHandler.add(Percent, 0.05, "Percent");

            Gene result = BAGA.Run();
            sw.Flush();
            sw.Close();
        }

        public static Gene Percent(Gene g1)
        {
            Gene tempGene = ByteArrayGA.CopyGene(g1);
            int i = 0;
            if (rnd.NextDouble() < 0.5)
            {
                i += 4;
            }
            int data = ByteArrayGA.ReadInt(ref i, tempGene.GeneData);

            //Console.Write(c1 + " => ");

            if (rnd.NextDouble() < 0.5)
            {
                data = (Int32)(data * 1.01);
            }
            else
            {
                data = (Int32)(data * 0.99);
            }

            byte[] bData = BitConverter.GetBytes(data);

            Array.Copy(bData, 0, tempGene.GeneData, i-4, bData.Length);
            
            i=0;
            int di1 = ByteArrayGA.ReadInt(ref i, g1.GeneData);
            int di2 = ByteArrayGA.ReadInt(ref i, g1.GeneData);
            i=0;
            int do1 = ByteArrayGA.ReadInt(ref i, tempGene.GeneData);
            int do2 = ByteArrayGA.ReadInt(ref i, tempGene.GeneData);

            Console.WriteLine(di1 + " => " + do1 + "\t : \t" + di2 + " => " + do2);
            foreach (byte b in g1.GeneData)
	        {
                Console.Write(b.ToString("x2") + " ");
	 
	        }
            Console.WriteLine();
            return tempGene;
        }

        public static Gene MyCrossover(Gene g1, Gene g2)
        {
            Gene tempGene = ByteArrayGA.CopyGene(g1);
            int i = 0;
            int j = 0;

            Int32 a1 = ByteArrayGA.ReadInt(ref i, g1.GeneData);
            Int32 b1 = ByteArrayGA.ReadInt(ref j, g2.GeneData);

            double r = rnd.NextDouble();

            Int32 data = (Int32)(a1 * r + (1 - r) * b1);
            
            byte[] bData = BitConverter.GetBytes(data);

            Array.Copy(bData, tempGene.GeneData, bData.Length);

            a1 = a1 = ByteArrayGA.ReadInt(ref i, g1.GeneData);
            b1 = b1 = ByteArrayGA.ReadInt(ref j, g2.GeneData);

            r = rnd.NextDouble();

            data = (Int32)(a1 * r + (1 - r) * b1);

            bData = BitConverter.GetBytes(data);

            Array.Copy(bData, 0, tempGene.GeneData, 4, bData.Length);

            return tempGene;
        }

      
        //return false to stop run.
        public static void Fitit(Gene g)
        {
            double SStot = 0;
            double SSreg = 0;
            double SSerr = 0;
            double yStreg = 0;

            for (byte j = 1; j < 10; j++)
            {
                yStreg += data[j, 1];
            }
            yStreg /= 10;

            int i = 0;
            int c1i = ByteArrayGA.ReadInt(ref i, g.GeneData);
            int c2i = ByteArrayGA.ReadInt(ref i, g.GeneData);

            double c1 = (double)(c1i) / (double)(Int32.MaxValue);
            double c2 = (double)(c2i) / (double)(Int32.MaxValue);
            c1 *= 20;
            c2 *= 20;

            g.fitness = 0;

            for (byte j = 1; j < 10; j++)
            {
                double x = data[j, 0];
                double y = data[j, 1];
                double yi =
                    c2 * x * x +
                    c1 * x;

                SStot += Math.Pow(yi - yStreg, 2);
                SSerr += Math.Pow(yi - y, 2);
                g.fitness = SSerr;//+= Math.Abs(Math.Log(yi) / Math.Log (y));// SSerr;
            }
            //g.fitness = 1.0 - (SSerr / SStot);//Math.Pow(res - y, 2);

        }

        public static void ForeachGeneration(int i, List<Gene> pop)
        {
            Console.WriteLine();
            if (double.IsInfinity(pop[0].fitness))
            {
                Console.ReadKey();
            }
            int j = 0;
            Int32 c1i = ByteArrayGA.ReadInt(ref j, pop[0].GeneData);
            Int32 c2i = ByteArrayGA.ReadInt(ref j, pop[0].GeneData);

            double c1 = (double)(c1i) / (double)(Int32.MaxValue);
            double c2 = (double)(c2i) / (double)(Int32.MaxValue);
            c1 *= 20;
            c2 *= 20;

            if (pop[0].fitness < bestfitnesssofar)
            {
                bestfitnesssofar = pop[0].fitness;
                sw.WriteLine(i.ToString() +
                    "\t" + pop[0].fitness.ToString("0.000000") +
                    "\t" + c1 +
                    "\t" + c2);
                sw.Flush();
                Console.WriteLine(i.ToString() + "\t" + pop[0].fitness.ToString("0.00000000") +
                    " | " + c1 +
                    " | " + c2
                    );
            }
            if (pop[0].fitness == 0)
            {
                Console.WriteLine("DONE!!");
                Console.ReadKey();
                Environment.Exit(0);
            }
        }
    }
}
